package cn.xinfei.xdecision.engine.api;

import cn.xinfei.xdecision.common.utils.exception.ApiException;
import cn.xinfei.xdecision.common.utils.exception.enums.ErrorCodeEnum;
import cn.xinfei.xdecision.common.utils.util.JsonUtils;
import cn.xinfei.xdecision.constant.EngineResultEnum;
import cn.xinfei.xdecision.engine.pre.PreProcess;
import cn.xinfei.xdecision.engine.runner.api.AbstractDecision;
import cn.xinfei.xdecision.engine.runner.api.DecisionDataApi;
import cn.xinfei.xdecision.engine.runner.api.DecisionReentryApi;
import cn.xinfei.xdecision.engine.runner.metadata.PrometheusTagsConstants;
import cn.xinfei.xdecision.engine.runner.service.DecisionParamCheckServer;
import cn.xinfei.xdecision.engine.runner.vo.EngineApiRequest;
import cn.xinfei.xdecision.model.DecisionApiRequest;
import cn.xinfei.xdecision.model.DecisionApiResponse;
import cn.xinfei.xdecision.model.DecisionDataApiRequest;
import com.alibaba.fastjson.JSONObject;
import com.dianping.cat.Cat;
import com.dianping.cat.message.Transaction;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Tags;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

@Slf4j
@RestController
@RequestMapping("engine")
public class XDecisionBatchApi {

    @Resource(name = "batchThreadPool")
    private ExecutorService batchThreadPool;

    @Autowired
    private DecisionDataApi decisionDataApi;


    @Value("${xdecision.decision.batchTimeout:60}")
    private long batchTimeout;

    @Autowired
    private XDecisionApi xDecisionApi;


    /**
     * 决策接口
     *
     * @param apiRequests
     * @return
     */
    @ApiOperation(value = "批量决策接口")
    @RequestMapping(value = "/batchDecision", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
    public List<DecisionApiResponse> batchDecision(@RequestBody List<DecisionApiRequest> apiRequests) {
        log.info("batchDecisionRequest:{}", JsonUtils.toJsonString(apiRequests));
        DecisionApiResponse decisionApiResponse = new DecisionApiResponse();
        List<DecisionApiResponse> responseList = new ArrayList<>(apiRequests.size());

        long start = System.currentTimeMillis();
        CountDownLatch countDownLatch = new CountDownLatch(apiRequests.size());
        try {
            for (int i = 0; i < apiRequests.size(); i++) {
                DecisionApiRequest request = apiRequests.get(i);
                batchThreadPool.submit(() -> {
                    long start1 = System.currentTimeMillis();
                    try {
                        DecisionApiResponse decision = xDecisionApi.decision(request);
                        responseList.add(decision);
                        Metrics.counter("xengine_batch_decision", Tags.of("status", "success", "result", decision.getResult())).increment();
                    } catch (Exception e) {
                        log.error("批量决策异常,msg={}", e.getMessage(), e);
                        Cat.logError(e);
                        Metrics.counter("xengine_batch_decision", Tags.of("status", "exception")).increment();
                    } finally {
                        long end = System.currentTimeMillis();
                        log.info("decisionDuration:{}", end - start1);
                        countDownLatch.countDown();
                        Metrics.gauge("xengine_decision_respond", PrometheusTagsConstants.XENGINE_API_RESPOND_TAGS, decisionApiResponse.getRespond());
                    }
                });
            }
            countDownLatch.await(batchTimeout, TimeUnit.SECONDS);
        } catch (Exception e) {
            log.error("批量决策异常,msg={}", e.getMessage(), e);
            Cat.logError(e);
        } finally {
            long end = System.currentTimeMillis();
            long batchDuration = end - start;
            log.info("batchDecisionDuration:{}", batchDuration);
            Metrics.gauge("xengine_batch_decision_respond", batchDuration);

        }
        return responseList;
    }


    /**
     * 查询决策结果接口
     *
     * @param apiRequests
     * @return
     */
    @ApiOperation(value = "决策结果批量查询接口")
    @RequestMapping(value = "/batchQuery", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
    public List<DecisionApiResponse> batchQuery(@RequestBody List<DecisionDataApiRequest> apiRequests) throws Exception {
        Transaction transaction = Cat.newTransaction("xengine", "query");
        List<DecisionApiResponse> responseList = new ArrayList<>();
        long start = System.currentTimeMillis();
        if (CollectionUtils.isEmpty(apiRequests) == false) {
            for (int i = 0; i < apiRequests.size(); i++) {

                DecisionApiResponse responses=null;
                DecisionDataApiRequest apiRequest = apiRequests.get(i);
                try {
                    if (StringUtils.isEmpty(apiRequest.getDecisionId()) || StringUtils.isEmpty(apiRequest.getRequestId())) {
                        log.error("REQUEST_ID_AND_DECISION_ID_CAN_NOT_NULL,apiRequest={}", JsonUtils.toJsonString(apiRequest));
                        continue;
                    }
                     responses = decisionDataApi.query(apiRequest);
                    log.info("queryRequest={}, response={}", JsonUtils.toJsonString(apiRequest), JsonUtils.toJsonString(responses));
                    responseList.add(responses);
                    transaction.setStatus(Transaction.SUCCESS);
                } catch (Exception e) {
                    log.error("查询决策结果接口异常，error={},apiRequest={}", e.getMessage(), JsonUtils.toJsonString(apiRequest), e);
                    Cat.logError(e); // 将异常上报到cat上
                    Metrics.counter("xengine_query_total", PrometheusTagsConstants.XENGINE_API_TOTAL_TAGS.and("code", "FAIL")).increment();
                } finally {
                    MDC.clear();
                }
            }
        }
        long end = System.currentTimeMillis();
        long respond = end - start;transaction.setDurationInMillis(respond);
        log.info("batchQueryDuration:{}", respond);
        Metrics.gauge("xengine_batchQuery_respond", PrometheusTagsConstants.XENGINE_API_RESPOND_TAGS, respond);
        transaction.complete();
        return responseList;
    }

}
